﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Configuration.Install;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Xml;
using Workflows.Designer.Properties;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.Win32;


namespace Workflows.Designer
{
    [RunInstaller(true)]
    public partial class SchemaInstaller : Installer
    {
        #region fields
        private EventLog _Log;
        private string _Server;
        private string _DbName;
        #endregion

        #region sqlex install parameters
        
        #endregion

        #region init
        public SchemaInstaller()
        {
            InitializeComponent();
            this.RegisterEventLog();
        }

        private void RegisterEventLog()
        {
            string eventSource = "Workflow";
            string eventLogName = "Workflow";
            
            if (!EventLog.SourceExists(eventSource))
            {
                EventLog.CreateEventSource(eventSource, eventLogName);
            }
            this._Log = new EventLog(eventLogName, ".", eventSource);
            this._Log.MaximumKilobytes = 4096;
            this._Log.ModifyOverflowPolicy(OverflowAction.OverwriteAsNeeded, 7);
        }

        private string GetDbConnStr()
        {
            if(this.Context.Parameters !=null && this.Context.Parameters.Count>0)
            {
                foreach(string key in this.Context.Parameters.Keys)
                {
                    if (key.ToUpper() == "SERVER")
                        this._Server = this.Context.Parameters[key];
                    if (key.ToUpper() == "DATABASE")
                        this._DbName = this.Context.Parameters[key];
                }
            }
            if(!string.IsNullOrEmpty(this._Server) && !string.IsNullOrEmpty(this._DbName))
            {
                return string.Format("Server={0};Database={1};Integrated Security=SSPI", this._Server, this._DbName);
            }
            else
            {
                return
                    "Data source=.\\SQLEXPRESS;AttachDbFileName=|DataDirectory|\\workflow.mdf;Integrated Security=true;User Instance=True";
            }
        }
        #endregion

        #region install
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            this._Log.WriteEntry("Trying to collect db connection settings...");
            if(this.Context.Parameters.Count>0)
            {
                foreach(string key in this.Context.Parameters.Keys)
                {
                    this._Log.WriteEntry(key + "=" + (string.IsNullOrEmpty(this.Context.Parameters[key])
                                                          ? string.Empty
                                                          : this.Context.Parameters[key]));
                }
            }
            //string installFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            //string initFilePath = Path.Combine(installFolderPath, "InstallState.ini");
            //if(File.Exists(initFilePath))
            //{
                
            //}
            //else
            //{
            //    throw new Exception(string.Format("Unable to find init file from {0}", initFilePath));
            //}
            string connStr = this.GetDbConnStr();
            if(!string.IsNullOrEmpty(connStr))
            {
                this._Log.WriteEntry("Connection string is: "+connStr);
            }
            else
            {
                this._Log.WriteEntry("Unable to get connection string", EventLogEntryType.Error);
            }
            // create database schema 
            try
            {
                this.CreateDatabase(this._Server, this._DbName);

                ResourceManager resMgr =
                    new ResourceManager(this.GetType().Namespace + ".Sqls", Assembly.GetAssembly(this.GetType()));
                string createTableSql = resMgr.GetString("Database_Workflow");
                createTableSql = createTableSql.Replace("$(DatabaseName)", this._DbName);
                this.RunSqlScripts(this._Server, this._DbName, createTableSql);
            }
            catch (Exception ex)
            {
                this._Log.WriteEntry(ex.ToString(), EventLogEntryType.Error);
                // throw new Exception(ex.ToString());
            }
        }

        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);
            string server = this.Context.Parameters["SERVER"];
            string dbName = this.Context.Parameters["DATABASE"];
            string assemblyFilePath = this.Context.Parameters["assemblypath"];
            string connStr = string.Empty;
            if ((!string.IsNullOrEmpty(server)) && (!string.IsNullOrEmpty(dbName)))
            {
                connStr = string.Format("server={0};database={1};integrated security=sspi", server, dbName);
            }
            if (string.IsNullOrEmpty(connStr))
            {
                this._Log.WriteEntry("Unable to get ConnectionString", EventLogEntryType.Information);
            }
            else if (string.IsNullOrEmpty(assemblyFilePath))
            {
                this._Log.WriteEntry("Unable to get assembly file path", EventLogEntryType.Information);
            }
            else
            {
                if (File.Exists(assemblyFilePath))
                {
                    try
                    {
                        string configFilePath = assemblyFilePath + ".config";
                        if (!File.Exists(configFilePath))
                        {
                            throw new Exception("Unable to find configuration file");
                        }
                        XmlDocument xDoc = new XmlDocument();
                        xDoc.Load(configFilePath);
                        XmlNode connNode = xDoc.SelectSingleNode("//connectionStrings/add[@name=\"Workflow\"]");
                        if (connNode != null)
                        {
                            XmlAttribute connStrAtt =
                                (XmlAttribute) connNode.Attributes.GetNamedItem("connectionString");
                            if (connStrAtt != null)
                            {
                                connStrAtt.Value = connStr;
                                xDoc.Save(configFilePath);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        this._Log.WriteEntry("Unable to save install state" + ex.ToString(), EventLogEntryType.Error);
                    }
                }
                else
                {
                    this._Log.WriteEntry("Unable to determine install path", EventLogEntryType.Information);
                }
            }
        }
        #endregion

        #region sql server installation
        private void CreateDatabase(string server, string dbName)
        {
            Server smoServer = new Server(server);
            if(!smoServer.Databases.Contains(dbName))
            {
                string connStr = string.Format("Server={0};Database=master;integrated security=sspi", server);
                SqlConnection conn = new SqlConnection(connStr);
                try
                {
                    conn.Open();
                    SqlCommand cmd = conn.CreateCommand();
                    cmd.CommandText = "create database " + dbName;
                    cmd.ExecuteNonQuery();
                }
                catch(Exception ex)
                {
                    throw new Exception("Unable to create database: "+ ex.ToString());
                }
                finally
                {
                    conn.Close();
                }
            }
        }

        private void RunSqlScripts(string server, string dbName, string sql)
        {
            try
            {
                Server smo = new Server(server);
                Database db = smo.Databases[dbName];
                db.ExecuteNonQuery(sql);
            }
            catch (Exception ex)
            {
                throw new Exception("Unable to create db schema: "+ex.ToString());
            }

            //SqlConnection conn=new SqlConnection(connStr);
            //try
            //{
            //    conn.Open();
            //    SqlCommand cmd = conn.CreateCommand();
            //    cmd.CommandText = sql;
            //    cmd.CommandType = CommandType.Text;
            //    cmd.ExecuteNonQuery();
            //}
            //catch(Exception ex)
            //{
            //    this._Log.WriteEntry(ex.ToString(),EventLogEntryType.Error);
            //}
            //finally
            //{
            //    conn.Close();
            //}
        }
        #endregion 

        #region sqlex installation
        private bool IsSqlExpressInstalled()
        {
            using (RegistryKey key=Registry.LocalMachine.OpenSubKey("Software\\Microsoft Sql Server\\",false))
            {
                if(key==null) return false;
                string[] strNames = key.GetSubKeyNames();
                if(strNames.Length==0) return false;

                foreach(string sName in strNames)
                {
                    if(sName.StartsWith("MSSQL."))
                    {
                        using(RegistryKey keyEdition=key.OpenSubKey(sName.ToString()+"\\Setup",false))
                        {
                            if((string)keyEdition.GetValue("Edition")=="Express Edition")
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// determine instances of sql server installed
        /// </summary>
        /// <param name="strInstanceArray"></param>
        /// <param name="strEditionArray"></param>
        /// <param name="strVersionArray"></param>
        /// <returns></returns>
        private int EnumSqlInstances(ref string[] strInstanceArray, ref string[] strEditionArray, ref string[] strVersionArray)
        {
            using (
                RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Microsoft SQL Server", false))
            {
                if (key == null) return 0;

                string[] strNames = key.GetSubKeyNames();
                if (strNames.Length == 0) return 0;
                int numberOfInstances = 0;
                foreach(string name in strNames)
                {
                    if (name.StartsWith("MSSQL."))
                        numberOfInstances++;
                }

                strInstanceArray=new string[numberOfInstances];
                strVersionArray=new string[numberOfInstances];
                strEditionArray=new string[numberOfInstances];
                int count = 0;
                foreach(string name in strNames)
                {
                    if(name.StartsWith("MSSQL."))
                    {
                        using (RegistryKey keyInstanceName=key.OpenSubKey(name.ToString(),false))
                        {
                            strInstanceArray[count] = (string) keyInstanceName.GetValue("");
                        }
                        using(RegistryKey keySetup=key.OpenSubKey(name.ToString()+"\\Setup\\",false))
                        {
                            strEditionArray[count] = (string) keySetup.GetValue("Edition");
                            strVersionArray[count] = (string) keySetup.GetValue("Version");
                        }
                        count++;
                    }
                }
                return count;
            }
        }

        private string BuildCommandLine()
        {
            StringBuilder cmdLine=new StringBuilder();

            return cmdLine.ToString();
        }
        #endregion

        #region update config file
        
        #endregion
    }
}
